import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import jdtprototype.JdtManipulator;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import util.Visitor;


public class JdtManipulatorTest {
	static IProject[] projects;
	static IProject project;
	static IJavaProject javaProject;
	static IPackageFragment packageFragment;
	static CompilationUnit unit;

	/**
	 * Initialises instances variables which will be used as sample parameters
	 * @throws Exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();	
		//get all projects in the workspace
		projects = root.getProjects();
		
		All: for(IProject tempProject : projects){
			if (tempProject.isNatureEnabled("org.eclipse.jdt.core.javanature")&&tempProject.isOpen()){
				//initialise project
				project = tempProject;
				//initialise javaProject
				javaProject = JavaCore.create(project);
				IPackageFragment[] packages = javaProject.getPackageFragments();
				for (IPackageFragment tempPackageFragment : packages) {
					if (tempPackageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
						//initialise packageFragment
						packageFragment=tempPackageFragment;
						ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();
						for(ICompilationUnit iUnit : iUnits){
							//initialise unit
							unit = Visitor.parse(iUnit);
							break All;
						}
					}
				}
			}
		}

	}

//	@Test
//	public void testGetCompilationUnits() {
//		
//	}

	/**
	 * Tests JdtManipulator.getIJavaProject(IProject project)
	 * @throws CoreException
	 */
	@Test
	public void testGetIJavaProject() throws CoreException {		
		for(IProject project : projects){
			//if the IProject is NOT of Java nature or not open, it should return null
			if (!project.isNatureEnabled("org.eclipse.jdt.core.javanature")||!project.isOpen()){
				assertEquals(null, JdtManipulator.getIJavaProject(project));
			}
			//if the IProject is of Java nature, it should return correct IJavaProject
			else {
				IJavaProject javaProject = JavaCore.create(project);
				assertEquals(javaProject, JdtManipulator.getIJavaProject(project));
			}
		}

	}

	/**
	 * Tests JdtManipulator.getIJavaProjects(IProject[] proejcts)
	 * 
	 * @throws CoreException
	 */
	@Test
	public void testGetIJavaProjects() throws CoreException {
		List <IJavaProject> projectList = new ArrayList<IJavaProject>();
		for(IProject project : projects){
			//if the IProject is of Java nature, it should return correct IJavaProject
			if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")&&project.isOpen()){
				IJavaProject javaProject = JavaCore.create(project);
				projectList.add(javaProject);
			}
		}
		//convert the list to an array
		IJavaProject [] javaProjects = projectList.toArray(new IJavaProject [projectList.size()]);
		assertArrayEquals(javaProjects, JdtManipulator.getIJavaProjects(projects));
	
	}

	/**
	 * Test JdtManipulator.getIPackageFragments(IJavaProject javaProject, boolean isSource)
	 * 
	 * @throws CoreException
	 */
	@Test
	public void testGetIPackageFragmentsIJavaProjectBoolean()
			throws CoreException {

		for (IProject project : projects) {
			if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")
					&& project.isOpen()) {

				IJavaProject javaProject = JavaCore.create(project);
				IPackageFragment[] packageFragments = javaProject
						.getPackageFragments();
				List<IPackageFragment> packageList = new ArrayList<IPackageFragment>();
				for (IPackageFragment pakcageFragment : packageFragments) {
					if (pakcageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
						packageList.add(pakcageFragment);
					}
				}
				//if parameter isSource is false
				assertArrayEquals(packageFragments,
						JdtManipulator.getIPackageFragments(javaProject, false));
				packageFragments = packageList
						.toArray(new IPackageFragment[packageList.size()]);
				//if parameter isSource is true
				assertArrayEquals(packageFragments,
						JdtManipulator.getIPackageFragments(javaProject, true));
			}
		}
	}

	/**
	 * Test JdtManipulator.getIPackageFragments(IProject project, boolean isSource) 
	 * 
	 * @throws CoreException
	 */
	@Test
	public void testGetIPackageFragmentsIProjectBoolean() throws CoreException {
		for (IProject project : projects) {
			IPackageFragment[] packageFragments = null;
			List<IPackageFragment> packageList = new ArrayList<IPackageFragment>();
			if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")
					&& project.isOpen()) {

				IJavaProject javaProject = JavaCore.create(project);
				packageFragments = javaProject.getPackageFragments();
				for (IPackageFragment pakcageFragment : packageFragments) {
					if (pakcageFragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
						packageList.add(pakcageFragment);
					}
				}

				// if parameter isSource is false
				assertArrayEquals(packageFragments,
						JdtManipulator.getIPackageFragments(project, false));
				packageFragments = packageList
						.toArray(new IPackageFragment[packageList.size()]);
				// if parameter isSource is true
				assertArrayEquals(packageFragments,
						JdtManipulator.getIPackageFragments(project, true));
			}
		}
	}

	/**
	 * Tests JdtManipulator.getICompilationUnits(IPackageFragment packageFragment)
	 * @throws JavaModelException
	 */
	@Test
	public void testGetICompilationUnits() throws JavaModelException {
		ICompilationUnit[] iUnits = packageFragment.getCompilationUnits();
		assertArrayEquals(iUnits, JdtManipulator.getICompilationUnits(packageFragment));
	}
	
	/**
	 * Test JdtManipulator.getCompilationUnits(IPackageFragment packageFragment)
	 * @throws CoreException
	 */
	@Test @Ignore
	public void testGetCompilationUnitsIPackageFragment() throws CoreException {
		ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();
		List <CompilationUnit> unitList = new ArrayList<CompilationUnit>();
		for(ICompilationUnit iUnit : iUnits){
			CompilationUnit unit = Visitor.parse(iUnit);
			unitList.add(unit);
		}
		assertArrayEquals(unitList.toArray(new CompilationUnit [unitList.size()]), JdtManipulator.getCompilationUnits(packageFragment));
	}

	@Test
	public void testGetCompilationUnitsIJavaProject() throws CoreException {
		IPackageFragment[] packageFragments = javaProject
				.getPackageFragments();
		List <CompilationUnit> expectedUnitList = new ArrayList<CompilationUnit>();
		for(IPackageFragment packageFragment : packageFragments){
			ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();			
			for(ICompilationUnit iUnit : iUnits){
				CompilationUnit unit = Visitor.parse(iUnit);
				expectedUnitList.add(unit);
			}
		}
		
		List <CompilationUnit> actualUnitList = JdtManipulator.getCompilationUnits(javaProject);
		assertEquals(expectedUnitList.size(), actualUnitList.size());
		for(int i =0; i<expectedUnitList.size();i++){
			//here, we compare the string format of the two compilation units
			assertEquals(expectedUnitList.get(i).toString(), actualUnitList.get(i).toString());
		}
		
	}

	@Test
	public void testGetTypeDeclarationCompilationUnit() {
		Visitor cVisitor = new Visitor();
		unit.accept(cVisitor);
		TypeDeclaration[] classes = cVisitor.getClasses().toArray(new TypeDeclaration[cVisitor.getClasses().size()]);
		assertArrayEquals(classes, JdtManipulator.getTypeDeclaration(unit));
	}

	@Test
	public void testGetTypeDeclarationIJavaProject() throws CoreException {
		IPackageFragment[] packageFragments = javaProject
				.getPackageFragments();
		List <TypeDeclaration> expectedTypeList = new ArrayList<TypeDeclaration>();
		for(IPackageFragment packageFragment : packageFragments){
			ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();			
			for(ICompilationUnit iUnit : iUnits){
				CompilationUnit unit = Visitor.parse(iUnit);
				Visitor cVisitor = new Visitor();
				unit.accept(cVisitor);
				expectedTypeList.addAll(cVisitor.getClasses());
			}
		}
		List<TypeDeclaration> actualTypeList = JdtManipulator
				.getTypeDeclaration(javaProject);
		assertEquals(expectedTypeList.size(), actualTypeList.size());
		for (int i = 0; i < expectedTypeList.size(); i++) {
			// here, we compare the string format of the two type declarations
			assertEquals(expectedTypeList.get(i).toString(), actualTypeList
					.get(i).toString());
		}
	}

	@Test
	public void testGetTypeDeclarationIPackageFragment() throws CoreException {
		List <TypeDeclaration> expectedTypeList = new ArrayList<TypeDeclaration>();
		ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();			
		for(ICompilationUnit iUnit : iUnits){
			CompilationUnit unit = Visitor.parse(iUnit);
			Visitor cVisitor = new Visitor();
			unit.accept(cVisitor);
			expectedTypeList.addAll(cVisitor.getClasses());
		}
		
		List<TypeDeclaration> actualTypeList = JdtManipulator
				.getTypeDeclaration(packageFragment);
		assertEquals(expectedTypeList.size(), actualTypeList.size());
		for (int i = 0; i < expectedTypeList.size(); i++) {
			// here, we compare the string format of the two type declarations
			assertEquals(expectedTypeList.get(i).toString(), actualTypeList
					.get(i).toString());
		}
	}

	@Test
	public void testGetMethodDeclarationCompilationUnit() {
		Visitor mVisitor = new Visitor();
		unit.accept(mVisitor);
//		retrieve method declarations from the compilation unit to which they belong
//		MethodDeclaration[] expectedMethods = mVisitor.getMethods().toArray(
//				new MethodDeclaration[mVisitor.getMethods().size()]);
		TypeDeclaration [] typeDeclarations = JdtManipulator.getTypeDeclaration(unit);
		List <MethodDeclaration> expectedMethodList = new ArrayList<MethodDeclaration>();
		for(TypeDeclaration type: typeDeclarations){
			//retrieve method declarations from the type declaration to which they belong,
			//this may result in a error when the compilation unit contains more than one type declaration
			expectedMethodList.addAll(Arrays.asList(type.getMethods()));
		}
		assertArrayEquals(expectedMethodList.toArray(new MethodDeclaration
				[expectedMethodList.size()]), JdtManipulator.getMethodDeclaration(unit));
//		assertArrayEquals(expectedMethods, JdtManipulator.getMethodDeclaration(unit))
	}

	@Test
	public void testGetMethodDeclarationIPackageFragment() throws CoreException {
		List <MethodDeclaration> expectedMethodList = new ArrayList<MethodDeclaration>();
		ICompilationUnit [] iUnits = packageFragment.getCompilationUnits();			
		for(ICompilationUnit iUnit : iUnits){
			CompilationUnit unit = Visitor.parse(iUnit);
			Visitor visitor = new Visitor();
			unit.accept(visitor);
			List <TypeDeclaration> typeList = new ArrayList<TypeDeclaration>(visitor.getClasses());
			for(TypeDeclaration typeDeclaration: typeList){
				expectedMethodList.addAll(Arrays.asList(typeDeclaration.getMethods()));
			}
		}
		
		List<MethodDeclaration> actualMethodList = JdtManipulator
				.getMethodDeclaration(packageFragment);
		assertEquals(expectedMethodList.size(), actualMethodList.size());
		for (int i = 0; i < expectedMethodList.size(); i++) {
			// here, we compare the string format of the two type declarations
			assertEquals(expectedMethodList.get(i).toString(), actualMethodList
					.get(i).toString());
		}
	}

	@Test
	public void testGetVariableDeclarationFragment() {
		Visitor vVisitor = new Visitor();
		unit.accept(vVisitor);
		VariableDeclarationFragment [] expectedVdf = vVisitor.getVariables()
				.toArray(
						new VariableDeclarationFragment[vVisitor.getVariables()
								.size()]);
		assertArrayEquals(expectedVdf, JdtManipulator.getVariableDeclarationStatement(unit));
	}

}
